/*
 * Author: vyouzhi <vyouzhi@163.com>
 * http://www.xda.cn
 *
 * File: libevent_lib.c
 * Create Date: 2011-09-22 09:29:21
 *
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <err.h>
#include <unistd.h>
#include <fcntl.h>
#include <malloc.h>

/* Libevent. */
#include <event.h>

#include "queue.h"
#include "libevent_lib.h"
#include "socket_lib.h"
#include "proc_data_lib.h"

void on_read(int fd, short ev, void *arg){
	struct client *client = (struct client *)arg;
	struct bufferq *bufferq;
	char *buf;
	int len;
	
	len = Socket_Recv(fd, &buf);
	if (len == 0) {
		printf("Client disconnected.\n");
                close(fd);
		event_del(&client->ev_read);
		free(client);
		return;
	}
	else if (len < 0) {
		close(fd);
		event_del(&client->ev_read);
		free(client);
		return;
	}

	bufferq = calloc(1, sizeof(*bufferq));
	if (bufferq == NULL)
		err(1, "malloc faild");
	bufferq->buf = buf;
	bufferq->len = len;
	bufferq->offset = 0;
	TAILQ_INSERT_TAIL(&client->writeq, bufferq, entries);

	event_add(&client->ev_write, NULL);
}

void on_write(int fd, short ev, void *arg){
	struct client *client = (struct client *)arg;
	struct bufferq *bufferq;
	int len, slen;
	char *sBuff, *ssBuff;

	bufferq = TAILQ_FIRST(&client->writeq);
	
	if (bufferq == NULL)
		return;

	sBuff = strstr(bufferq->buf, "<?xml");	

	if(sBuff == NULL){
		TAILQ_REMOVE(&client->writeq, bufferq, entries);
		free(bufferq);
		event_del(&client->ev_read);
		close(fd);

		return ;
	}

	slen = strlen(sBuff);
	ssBuff = SwitchData(sBuff);
	
	if(ssBuff == NULL){
		TAILQ_REMOVE(&client->writeq, bufferq, entries);
		free(bufferq);
		event_del(&client->ev_read);
		close(fd);

		return ;
	}
	slen = strlen(ssBuff);

	len = Socket_Send(fd, ssBuff, slen);

	if (len == -1) {
		if (errno == EINTR || errno == EAGAIN) {
			event_add(&client->ev_write, NULL);
			return;
		}
		else {
			err(1, "write");
		}
	}
	TAILQ_REMOVE(&client->writeq, bufferq, entries);

	free(bufferq);
	free(ssBuff);	
	event_del(&client->ev_read);
	close(fd);
}

void on_accept(int fd, short ev, void *arg){
	int client_fd;
	struct sockaddr_in client_addr;
	socklen_t client_len = sizeof(client_addr);
	struct client *client;

	/* Accept the new connection. */
	client_fd = accept(fd, (struct sockaddr *)&client_addr, &client_len);
	if (client_fd == -1) {
		warn("accept failed");
		return;
	}

	/* Set the client socket to non-blocking mode. */
	if (SetNonBlocking(client_fd) < 0)
		warn("failed to set client socket non-blocking");

	/* We've accepted a new client, allocate a client object to
	 * maintain the state of this client. */
	client = calloc(1, sizeof(*client));
	if (client == NULL)
		err(1, "malloc failed");

	/* Setup the read event, libevent will call on_read() whenever
	 * the clients socket becomes read ready.  We also make the
	 * read event persistent so we don't have to re-add after each
	 * read. */
	event_set(&client->ev_read, client_fd, EV_READ|EV_PERSIST, on_read, 
	    client);

	/* Setting up the event does not activate, add the event so it
	 * becomes active. */
	event_add(&client->ev_read, NULL);

	/* Create the write event, but don't add it until we have
	 * something to write. */
	event_set(&client->ev_write, client_fd, EV_WRITE, on_write, client);

	/* Initialize the clients write queue. */
	TAILQ_INIT(&client->writeq);

	/*printf("Accepted connection from \n");
               inet_ntoa(client_addr.sin_addr));*/
}

void Event_Init(void){
	int listen_fd;

	/* The socket accept event. */
	struct event *ev_accept = (struct event*)malloc(sizeof(struct event));

	/* Initialize libevent. */
	event_init();

	/* Create our listening socket. This is largely boiler plate
	 * code that I'll abstract away in the future. */
	listen_fd = Socket_Init();
	if (listen_fd < 0)
		err(1, "listen failed");

	event_set(ev_accept, listen_fd, EV_READ|EV_PERSIST, on_accept, NULL);
	event_add(ev_accept, NULL);

	/* Start the libevent event loop. */
	event_dispatch();

}
/* vim: set ts=4 sw=4: */

